package com.spice.service.entity.job;

import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSONObject;
import com.spice.service.schedule.ScheduledTasks;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.concurrent.CopyOnWriteArrayList;

@Slf4j
public class JobExecutor {
    /**
     * 线程资源数
     */
    private static Integer threadResource = Runtime.getRuntime().availableProcessors() / 3 > 0 ?
            Runtime.getRuntime().availableProcessors() / 3 : 1;
    /**
     * 当前可用线程资源数
     */
    private static volatile Integer activeThreadResource = threadResource;
    /**
     * 已完成工作的缓存上限
     */
    private static final int MAX_DONE_JOB_NUM = 10;
    /**
     * 是否已经布置持久化任务
     */
    private static boolean hasSaved = false;
    /**
     * 尚未初始化的新工作FIFO队列
     */
    private static volatile PriorityQueue<Job> newJobList = new PriorityQueue<>();
    /**
     * 正在初始化的工作队列
     */
    private static volatile CopyOnWriteArrayList<Job> initializingJobList = new CopyOnWriteArrayList<>();
    /**
     * 已经初始化完毕，随时准备运行的工作FIFO队列
     */
    private static volatile PriorityQueue<Job> readyJobList = new PriorityQueue<>();
    /**
     * 正在运行的工作队列
     */
    private static volatile CopyOnWriteArrayList<Job> runningJobList = new CopyOnWriteArrayList<>();
    /**
     * 已经结束了的工作队列
     */
    public static volatile CopyOnWriteArrayList<Job> doneJobList = new CopyOnWriteArrayList<>();

    private static boolean active = false;

    private static final Object NEW_JOB_LIST_LOCK = new Object();

    private static void addNewJob(Job job) {
        synchronized (NEW_JOB_LIST_LOCK) {
            newJobList.add(job);
        }
    }

    private static Job pollNewJob() {
        synchronized (NEW_JOB_LIST_LOCK) {
            return newJobList.poll();
        }
    }

    private static final Object READY_JOB_LIST_LOCK = new Object();

    private static void addReadyJob(Job job) {
        synchronized (READY_JOB_LIST_LOCK) {
            readyJobList.add(job);
        }
    }

    private static Job pollReadyJob() {
        synchronized (READY_JOB_LIST_LOCK) {
            return readyJobList.poll();
        }
    }

    public static void addJob(Job job) {
        addNewJob(job);
        if (!active) {
            new Thread(JobExecutor::toActive).start();
            active = true;
        }
    }

    private static void executeJob() {
        // 执行job
        if (readyJobList.size() > 0) {
            Job readyJob = pollReadyJob();
            log.info(readyJob.getId() + " 正在运行");
            readyJob.run();
            runningJobList.add(readyJob);
        } else if (newJobList.size() > 0) {
            Job newJob = pollNewJob();
            log.info(newJob.getId() + " 正在初始化");
            newJob.initialize();
            initializingJobList.add(newJob);
        }
    }

    private static void freeDoneJobs() {
        ScheduledTasks.saveDoneJobRecord();
    }

    private static boolean isDoneJobsNeedSave() {
        if (MAX_DONE_JOB_NUM <= doneJobList.size()) {
            // 如果超出上限则下达任务
            if (!hasSaved) {
                hasSaved = true;
                return true;
            }
        } else {
            if (hasSaved) {
                hasSaved = false;
            }
        }
        return false;
    }

    private static void freeJobs() {
        // 释放已经完成的job
        if (runningJobList.size() > 0) {
            runningJobList.removeIf(
                    job -> {
                        if (job.getStatus().isDone()) {
                            log.info(job.getId() + " 已经完成");
                            doneJobList.add(job);
                            // 确认完成队列是否已满
                            if (isDoneJobsNeedSave()) {
                                freeDoneJobs();
                            }
                            plusActiveThreadResource();
                            return true;
                        } else {
                            return false;
                        }
                    });
        } else if (initializingJobList.size() > 0) {
            initializingJobList.removeIf(
                    job -> {
                        if (job.getStatus().isReady()) {
                            log.info(job.getId() + " 已经准备就绪");
                            addReadyJob(job);
                            plusActiveThreadResource();
                            return true;
                        } else {
                            return false;
                        }
                    });
        }
    }

    private static boolean isAllJobDone() {
        return newJobList.size() == 0
                && initializingJobList.size() == 0
                && readyJobList.size() == 0
                && runningJobList.size() == 0;
    }

    private static final Object activeThreadResourceLock = new Object();

    private static void plusActiveThreadResource() {
        synchronized (activeThreadResourceLock) {
            activeThreadResource++;
        }
    }

    private static void lessActiveThreadResource() {
        synchronized (activeThreadResourceLock) {
            activeThreadResource--;
        }
    }

    private static void toActive() {
        while (!isAllJobDone()) {
            if (activeThreadResource > 0) {
                executeJob();
                lessActiveThreadResource();
            }
            freeJobs();
        }
        // 任务执行结束 进入休眠状态
        active = false;
    }

    public static Job searchDoneJob(String id) {
        Job target = null;
        // 从内存找
        for (Job job : doneJobList) {
            if (id.equals(job.id)) {
                target = job;
                break;
            }
        }

        if (target == null) {
            // 内存没找到，就从磁盘找
            for (File file : FileUtil.ls(SaveRecordJob.historyJobFolder.getAbsolutePath())) {
                if (file.getName().equals(id)) {
                    String s = FileUtil.readUtf8String(file);
                    target = JSONObject.parseObject(s, Job.class);
                }
            }
        }

        return target;
    }

    public static void main(String[] args) throws InterruptedException {

        Job job = JobExecutor.searchDoneJob("Job_1645247164992_6dcd");
        System.out.println(job);

//        Job job = new Job();
//        System.out.println(job.getId());
//        Job job1 = new Job();
//        System.out.println(job1.getId());
////        SaveRecordJob saveRecordJob = new SaveRecordJob();
////        System.out.println(saveRecordJob.getId());
//        JobExecutor.addJob(job);
//        JobExecutor.addJob(job1);
////        JobExecutor.addJob(saveRecordJob);

//        for (int i = 0; i < 20; i++) {
//            JobExecutor.addJob(new Job());
//        }
//
//        Thread.sleep(1000 * 3);
//        System.out.println(JobExecutor.doneJobList.size());
//        System.out.println(JobExecutor.active);
//
//        for (int i = 0; i < 20; i++) {
//            JobExecutor.addJob(new Job());
//        }
//
//        Thread.sleep(1000 * 3);
//        System.out.println(JobExecutor.doneJobList.size());
//        System.out.println(JobExecutor.active);
    }
}
